﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FernUniHagen.CompilerExtension.Attributes;

using FernUniHagen.CompilerExtension.DataStructures;
using FernUniHagen.CompilerExtension.Functions;
using Roslyn.Compilers.Common;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;
using FernUniHagen.CompilerExtension.Values;

namespace FernUniHagen.CompilerExtension
{
    public class AttributeWalker : SyntaxWalker
    {
        private ISolution _solution;
        private ISemanticModel _semanticModel;
        private IDocument _doc;
        private List<SyntaxNodeInfo> annotatedSyntaxNodes = new List<SyntaxNodeInfo>();

        /// <summary>
        /// Finds all elements which are annotated with a multiplicity annotation
        /// Uses walker to do so
        /// </summary>
        /// <param name="solution">the solution containing the annotated elements</param>
        /// <param name="project">the project containing the annotated elements</param>
        /// <returns>List of all annotated elements among some other information (see SyntaxNodeInfo)</returns>
        public static List<SyntaxNodeInfo> GetMultiplicityAnnotatedElements(ISolution solution, IProject project)
        {
            List<SyntaxNodeInfo> annotatedSyntaxNodes = new List<SyntaxNodeInfo>();
            AttributeWalker attributeWalker;
            foreach (var document in project.Documents)
            {
                ISemanticModel semanticModel = document.GetSemanticModel();
                var root = (CompilationUnitSyntax)document.GetSyntaxRoot();
                attributeWalker = new AttributeWalker(solution, semanticModel, document);
                attributeWalker.Visit(root);
                annotatedSyntaxNodes.AddRange(attributeWalker.AnnotatedSyntaxNodes);
            }
            return annotatedSyntaxNodes;
        }


        public List<SyntaxNodeInfo> AnnotatedSyntaxNodes
        {
            get { return annotatedSyntaxNodes; }
            set { annotatedSyntaxNodes = value; }
        }
     


     
        private AttributeWalker(ISolution solution, ISemanticModel semanticModel, IDocument doc)
        {
            _solution = solution;
            _semanticModel = semanticModel;
            _doc = doc;

        }

        /// <summary>
        /// Retrieves all the nodes which are annotated with a multiplicity
        /// </summary>
        /// <param name="node"></param>
        public override void VisitAttribute(AttributeSyntax node)
        {
            base.VisitAttribute(node);
            AttributeType type =  Utilities.GetAttributeType(_semanticModel, node);
           
            if (type == AttributeType.Any || type == AttributeType.Option)
            {
                SyntaxNodeInfo nodeInfo = null;
                CommonSyntaxNode correctlyTypedNode = node.GetDeclarationType();
         
                nodeInfo = new SyntaxNodeInfo(_doc, correctlyTypedNode);
                annotatedSyntaxNodes.Add(nodeInfo);
            } 
            
        }

        public override void VisitCastExpression(CastExpressionSyntax node)
        {
            base.VisitCastExpression(node);
            INamedTypeSymbol typeSymbol = _semanticModel.Compilation.GetTypeByMetadataName(Constants.MultiplicityType);

            ITypeSymbol nodeType = _semanticModel.GetTypeInformation( node);

            //Compares the full qualified names
            if(nodeType.Interfaces.Contains(typeSymbol))
            {
                BlockSyntax blockSyntax = node.FirstAncestorOrSelf<BlockSyntax>();
                StatementSyntax stmt = node.FirstAncestorOrSelf<StatementSyntax>();
                SyntaxNodeInfo nodeInfo = new SyntaxNodeInfo(_doc, blockSyntax, node, stmt,null);
                annotatedSyntaxNodes.Add(nodeInfo);
            }

            //As we go through all casts anyway, we can also try to find the typecasts on attributes



            
        }

        public override void VisitElementAccessExpression(ElementAccessExpressionSyntax node)
        {
            base.VisitElementAccessExpression(node);
            if (node.ToString().Contains("[OptionAttribute]"))
            {
                BlockSyntax blockSyntax = node.FirstAncestorOrSelf<BlockSyntax>();
                StatementSyntax stmt = node.FirstAncestorOrSelf<StatementSyntax>();
                SyntaxNodeInfo nodeInfo = new SyntaxNodeInfo(_doc, blockSyntax, node, stmt,null); 
                annotatedSyntaxNodes.Add(nodeInfo);
            }
        //    Utilities.FindDecleration(_semanticModel, (IdentifierNameSyntax)node.Expression);
        }
       
    }
}
